/* * Copyright (C) 2016 Amit Shekhar * Copyright (C) 2011 Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.rxandroidnetworking; import android.net.TrafficStats; import com.androidnetworking.common.ANConstants; import com.androidnetworking.common.ANResponse; import com.androidnetworking.common.ConnectionClassManager; import com.androidnetworking.common.RequestType; import com.androidnetworking.error.ANError; import com.androidnetworking.internal.InternalNetworking; import com.androidnetworking.internal.RequestProgressBody; import com.androidnetworking.internal.ResponseProgressBody; import com.androidnetworking.utils.SourceCloseUtil; import com.androidnetworking.utils.Utils; import java.io.File; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; import okhttp3.Call; import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import rx.Observable; import rx.Producer; import rx.Subscriber; import rx.Subscription; import rx.exceptions.Exceptions; import static com.androidnetworking.common.Method.DELETE; import static com.androidnetworking.common.Method.GET; import static com.androidnetworking.common.Method.HEAD; import static com.androidnetworking.common.Method.PATCH; import static com.androidnetworking.common.Method.POST; import static com.androidnetworking.common.Method.PUT; /** * Created by Prashant Gupta on 25-07-2016. */ @SuppressWarnings("unchecked") public class RxInternalNetworking { public static <T> Observable<T> generateSimpleObservable(RxANRequest request) { Request okHttpRequest; Request.Builder builder = new Request.Builder().url(request.getUrl()); InternalNetworking.addHeadersToRequestBuilder(builder, request); RequestBody requestBody; switch (request.getMethod()) { case GET: { builder = builder.get(); break; } case POST: { requestBody = request.getRequestBody(); builder = builder.post(requestBody); break; } case PUT: { requestBody = request.getRequestBody(); builder = builder.put(requestBody); break; } case DELETE: { requestBody = request.getRequestBody(); builder = builder.delete(requestBody); break; } case HEAD: { builder = builder.head(); break; } case PATCH: { requestBody = request.getRequestBody(); builder = builder.patch(requestBody); break; } } if (request.getCacheControl() != null) { builder.cacheControl(request.getCacheControl()); } okHttpRequest = builder.build(); if (request.getOkHttpClient() != null) { request.setCall(request .getOkHttpClient() .newBuilder() .cache(InternalNetworking.sHttpClient.cache()) .build() .newCall(okHttpRequest)); } else { request.setCall(InternalNetworking.sHttpClient.newCall(okHttpRequest)); } return Observable.create(new ANOnSubscribe<T>(request)); } public static <T> Observable<T> generateDownloadObservable(final RxANRequest request) { Request okHttpRequest; Request.Builder builder = new Request.Builder().url(request.getUrl()); InternalNetworking.addHeadersToRequestBuilder(builder, request); builder = builder.get(); if (request.getCacheControl() != null) { builder.cacheControl(request.getCacheControl()); } okHttpRequest = builder.build(); OkHttpClient okHttpClient; if (request.getOkHttpClient() != null) { okHttpClient = request .getOkHttpClient() .newBuilder() .cache(InternalNetworking.sHttpClient.cache()) .addNetworkInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Response originalResponse = chain.proceed(chain.request()); return originalResponse.newBuilder() .body(new ResponseProgressBody(originalResponse.body(), request.getDownloadProgressListener())) .build(); } }).build(); } else { okHttpClient = InternalNetworking.sHttpClient.newBuilder() .addNetworkInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Response originalResponse = chain.proceed(chain.request()); return originalResponse.newBuilder() .body(new ResponseProgressBody(originalResponse.body(), request.getDownloadProgressListener())) .build(); } }).build(); } request.setCall(okHttpClient.newCall(okHttpRequest)); return Observable.create(new ANOnSubscribe<T>(request)); } public static <T> Observable<T> generateMultipartObservable(final RxANRequest request) { return Observable.create(new ANOnSubscribe<T>(request)); } static final class ANOnSubscribe<T> implements Observable.OnSubscribe<T> { private final RxANRequest request; public ANOnSubscribe(RxANRequest request) { this.request = request; } @Override public void call(Subscriber<? super T> subscriber) { switch (request.getRequestType()) { case RequestType.SIMPLE: ANResolver<T> anResolver = new ANResolver<>(request, subscriber); subscriber.add(anResolver); subscriber.setProducer(anResolver); break; case RequestType.DOWNLOAD: DownloadANResolver<T> downloadANResolver = new DownloadANResolver<>(request, subscriber); subscriber.add(downloadANResolver); subscriber.setProducer(downloadANResolver); break; case RequestType.MULTIPART: MultipartANResolver<T> multipartANResolver = new MultipartANResolver<>(request, subscriber); subscriber.add(multipartANResolver); subscriber.setProducer(multipartANResolver); break; } } } static final class ANResolver<T> extends AtomicBoolean implements Subscription, Producer { private final Call call; private final RxANRequest request; private final Subscriber<? super T> subscriber; ANResolver(RxANRequest request, Subscriber<? super T> subscriber) { this.request = request; this.call = request.getCall(); this.subscriber = subscriber; } @Override public void request(long n) { if (n < 0) throw new IllegalArgumentException("n < 0: " + n); if (n == 0) return; // Nothing to do when requesting 0. if (!compareAndSet(false, true)) return; // Request was already triggered. Response okHttpResponse = null; try { final long startTime = System.currentTimeMillis(); final long startBytes = TrafficStats.getTotalRxBytes(); okHttpResponse = call.execute(); final long timeTaken = System.currentTimeMillis() - startTime; if (okHttpResponse.cacheResponse() == null) { final long finalBytes = TrafficStats.getTotalRxBytes(); final long diffBytes; if (startBytes == TrafficStats.UNSUPPORTED || finalBytes == TrafficStats.UNSUPPORTED) { diffBytes = okHttpResponse.body().contentLength(); } else { diffBytes = finalBytes - startBytes; } ConnectionClassManager.getInstance().updateBandwidth(diffBytes, timeTaken); Utils.sendAnalytics(request.getAnalyticsListener(), timeTaken, (request.getRequestBody() != null && request.getRequestBody().contentLength() != 0) ? request.getRequestBody().contentLength() : -1, okHttpResponse.body().contentLength(), false); } else if (request.getAnalyticsListener() != null) { if (okHttpResponse.networkResponse() == null) { Utils.sendAnalytics(request.getAnalyticsListener(), timeTaken, 0, 0, true); } else { Utils.sendAnalytics(request.getAnalyticsListener(), timeTaken, (request.getRequestBody() != null && request.getRequestBody().contentLength() != 0) ? request.getRequestBody().contentLength() : -1, 0, true); } } if (okHttpResponse.code() >= 400) { if (!subscriber.isUnsubscribed()) { subscriber.onError(Utils.getErrorForServerResponse(new ANError(okHttpResponse), request, okHttpResponse.code())); } } else { ANResponse<T> response = request.parseResponse(okHttpResponse); if (!response.isSuccess()) { if (!subscriber.isUnsubscribed()) { subscriber.onError(response.getError()); } } else { if (!subscriber.isUnsubscribed()) { subscriber.onNext(response.getResult()); } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } } } } catch (IOException ioe) { if (!subscriber.isUnsubscribed()) { subscriber.onError(Utils.getErrorForConnection(new ANError(ioe))); } } catch (Exception e) { Exceptions.throwIfFatal(e); if (!subscriber.isUnsubscribed()) { subscriber.onError(Utils.getErrorForNetworkOnMainThreadOrConnection(e)); } } finally { SourceCloseUtil.close(okHttpResponse, request); } } @Override public void unsubscribe() { call.cancel(); } @Override public boolean isUnsubscribed() { return call.isCanceled(); } } static final class DownloadANResolver<T> extends AtomicBoolean implements Subscription, Producer { private final Call call; private final RxANRequest request; private final Subscriber<? super T> subscriber; DownloadANResolver(RxANRequest request, Subscriber<? super T> subscriber) { this.request = request; this.call = request.getCall(); this.subscriber = subscriber; } @Override public void request(long n) { if (n < 0) throw new IllegalArgumentException("n < 0: " + n); if (n == 0) return; // Nothing to do when requesting 0. if (!compareAndSet(false, true)) return; // Request was already triggered. Response okHttpResponse; try { final long startTime = System.currentTimeMillis(); final long startBytes = TrafficStats.getTotalRxBytes(); okHttpResponse = request.getCall().execute(); Utils.saveFile(okHttpResponse, request.getDirPath(), request.getFileName()); final long timeTaken = System.currentTimeMillis() - startTime; if (okHttpResponse.cacheResponse() == null) { final long finalBytes = TrafficStats.getTotalRxBytes(); final long diffBytes; if (startBytes == TrafficStats.UNSUPPORTED || finalBytes == TrafficStats.UNSUPPORTED) { diffBytes = okHttpResponse.body().contentLength(); } else { diffBytes = finalBytes - startBytes; } ConnectionClassManager.getInstance().updateBandwidth(diffBytes, timeTaken); Utils.sendAnalytics(request.getAnalyticsListener(), timeTaken, -1, okHttpResponse.body().contentLength(), false); } else if (request.getAnalyticsListener() != null) { Utils.sendAnalytics(request.getAnalyticsListener(), timeTaken, -1, 0, true); } if (okHttpResponse.code() >= 400) { if (!subscriber.isUnsubscribed()) { subscriber.onError(Utils.getErrorForServerResponse(new ANError(okHttpResponse), request, okHttpResponse.code())); } } else { if (!subscriber.isUnsubscribed()) { ANResponse<T> response = (ANResponse<T>) ANResponse.success(ANConstants.SUCCESS); subscriber.onNext(response.getResult()); } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } } } catch (IOException ioe) { try { File destinationFile = new File(request.getDirPath() + File.separator + request.getFileName()); if (destinationFile.exists()) { destinationFile.delete(); } } catch (Exception e) { e.printStackTrace(); } if (!subscriber.isUnsubscribed()) { subscriber.onError(Utils.getErrorForConnection(new ANError(ioe))); } } catch (Exception e) { Exceptions.throwIfFatal(e); if (!subscriber.isUnsubscribed()) { subscriber.onError(Utils.getErrorForNetworkOnMainThreadOrConnection(e)); } } } @Override public void unsubscribe() { call.cancel(); } @Override public boolean isUnsubscribed() { return call.isCanceled(); } } static final class MultipartANResolver<T> extends AtomicBoolean implements Subscription, Producer { private final RxANRequest request; private final Subscriber<? super T> subscriber; MultipartANResolver(RxANRequest request, Subscriber<? super T> subscriber) { this.request = request; this.subscriber = subscriber; } @Override public void request(long n) { if (n < 0) throw new IllegalArgumentException("n < 0: " + n); if (n == 0) return; // Nothing to do when requesting 0. if (!compareAndSet(false, true)) return; // Request was already triggered. Response okHttpResponse = null; Request okHttpRequest; try { Request.Builder builder = new Request.Builder().url(request.getUrl()); InternalNetworking.addHeadersToRequestBuilder(builder, request); final RequestBody requestBody = request.getMultiPartRequestBody(); final long requestBodyLength = requestBody.contentLength(); builder = builder.post(new RequestProgressBody(requestBody, request.getUploadProgressListener())); if (request.getCacheControl() != null) { builder.cacheControl(request.getCacheControl()); } okHttpRequest = builder.build(); if (request.getOkHttpClient() != null) { request.setCall(request .getOkHttpClient() .newBuilder() .cache(InternalNetworking.sHttpClient.cache()) .build() .newCall(okHttpRequest)); } else { request.setCall(InternalNetworking.sHttpClient.newCall(okHttpRequest)); } final long startTime = System.currentTimeMillis(); okHttpResponse = request.getCall().execute(); final long timeTaken = System.currentTimeMillis() - startTime; if (request.getAnalyticsListener() != null) { if (okHttpResponse.cacheResponse() == null) { Utils.sendAnalytics(request.getAnalyticsListener(), timeTaken, requestBodyLength, okHttpResponse.body().contentLength(), false); } else { if (okHttpResponse.networkResponse() == null) { Utils.sendAnalytics(request.getAnalyticsListener(), timeTaken, 0, 0, true); } else { Utils.sendAnalytics(request.getAnalyticsListener(), timeTaken, requestBodyLength != 0 ? requestBodyLength : -1, 0, true); } } } if (okHttpResponse.code() >= 400) { if (!subscriber.isUnsubscribed()) { subscriber.onError(Utils.getErrorForServerResponse(new ANError(okHttpResponse), request, okHttpResponse.code())); } } else { ANResponse<T> response = request.parseResponse(okHttpResponse); if (!response.isSuccess()) { if (!subscriber.isUnsubscribed()) { subscriber.onError(response.getError()); } } else { if (!subscriber.isUnsubscribed()) { subscriber.onNext(response.getResult()); } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } } } } catch (IOException ioe) { if (!subscriber.isUnsubscribed()) { subscriber.onError(Utils.getErrorForConnection(new ANError(ioe))); } } catch (Exception e) { Exceptions.throwIfFatal(e); if (!subscriber.isUnsubscribed()) { subscriber.onError(Utils.getErrorForNetworkOnMainThreadOrConnection(e)); } } finally { SourceCloseUtil.close(okHttpResponse, request); } } @Override public void unsubscribe() { if (request.getCall() != null) { request.getCall().cancel(); } } @Override public boolean isUnsubscribed() { return request.getCall() != null && request.getCall().isCanceled(); } } }